/*
 * tcp_connection.h
 *
 *  Created on: 2016-4-2
 *      Author: ZhangXiyin
 */

#ifndef EYAS_SOURCES_IO_SOCKET_TCP_TCP_CONNECTION_H_
#define EYAS_SOURCES_IO_SOCKET_TCP_TCP_CONNECTION_H_

#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>

#include "buffer/iobuffer.h"

#include "../../conf/def.h"
#include "../../base/connection_base.hpp"
#include "../../base/data_handler_base.hpp"

namespace eyas {
namespace io {

template <typename func>
class tcp_connection_impl : public data_handler_base{
public:
	tcp_connection_impl(func& f):func_(f){}
	virtual ~tcp_connection_impl(){}
public:
	virtual void on_data_received(eyas::buffer::iobuffer& data, const error_code& error){
		func_(data, error);
	}
private:
	func func_;
};

class tcp_connection : public connection_base, public boost::enable_shared_from_this<tcp_connection>{
public:
	tcp_connection(tcp_socket_ptr tcp_skt_ptr);
	virtual ~tcp_connection();

private:
	friend class tcp_connection_impl;
	tcp_socket_ptr tcp_skt_ptr_;
	boost::asio::io_service& io_service_;
	boost::asio::ip::tcp::socket& socket_;
	boost::asio::ip::tcp::endpoint& endpoint_;
	bool disable_;

	boost::recursive_mutex mutex_;
	//packet_factory_ptr pkt_factory_ptr_;
	packet_decoder_ptr pkt_decoder_ptr_;
	int32_t wait_recv_len_;

	// read buffer
	eyas::buffer::iobuffer read_buffer_;

	data_handler_ptr dat_handler_ptr_;

	void handle_write(eyas::buffer::iobuffer_slices_ptr& slicesPtr, const boost::system::error_code& error, size_t bytes_transferred);
	void handle_read(eyas::buffer::iobuffer_slices_ptr& bufferSlicesPtr, const boost::system::error_code& error, size_t bytes_transferred);

	void do_error(const boost::system::error_code& error);
	void ready_for_next_read();
public:
	template <class packet_decoder>
	bool launch(void (*data_recv_callback)(
				tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error));
	template <class packet_decoder,
		typename B1,
		typename A1>
	bool launch(void (*data_recv_callback)(
				tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error, B1 b1),
			A1 a1);
	template <class packet_decoder,
		typename B1, typename B2,
		typename A1, typename A2>
	bool launch(void (*data_recv_callback)(
				tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error,
				B1 b1, B2 b2),
			A1 a1, A2 a2);
	template <class packet_decoder,
		typename B1, typename B2, typename B3,
		typename A1, typename A2, typename A3>
	bool launch(void (*data_recv_callback)(
				tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error,
				B1 b1, B2 b2, B3 b3),
			A1 a1, A2 a2, A3 a3);


	template <class packet_decoder,
		class T, typename T_Obj>
	bool launch(void (T::*data_recv_callback)(
				tcp_connection_ptr const & conn_ptr, const eyas::io::error_code &error),
			T_Obj obj);
	template <class packet_decoder,
		class T, typename T_Obj,
		typename B1,
		typename A1>
	bool launch(void (T::*connected_callback)(
				tcp_connection_ptr const & conn_ptr, const eyas::io::error_code &error,
				B1 b1),
			T_Obj obj,
			A1 a1);
	template <class packet_decoder,
		class T, typename T_Obj,
		typename B1, typename B2,
		typename A1, typename A2>
	bool launch(void (T::*connected_callback)(
				tcp_connection_ptr const & conn_ptr, const eyas::io::error_code &error,
				B1 b1, B2 b2),
			T_Obj obj,
			A1 a1, A2 a2);
	template <class packet_decoder,
		class T, typename T_Obj,
		typename B1, typename B2, typename B3,
		typename A1, typename A2, typename A3>
	bool launch(void (T::*connected_callback)(
				tcp_connection_ptr const & conn_ptr, const eyas::io::error_code &error,
				B1 b1, B2 b2, B3 b3),
			T_Obj obj,
			A1 a1, A2 a2, A3 a3);
	virtual void close();

	virtual void write(packet_encoder_base& packet);
};

template <typename F>
data_handler_ptr generate_handler(F f){
	return eyas::gc::create_new<tcp_connection_impl<F>>(f);
}

template <class packet_decoder>
bool tcp_connection::launch(void (*data_recv_callback)(
			tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error)){
    boost::recursive_mutex::scoped_lock lck(this->mutex_);
    this->disable_ = false;
    this->pkt_decoder_ptr_ = eyas::gc::create_new<packet_decoder>();
    this->dat_handler_ptr_ = generate_handler(
            boost::bind(data_recv_callback, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)
        );
    this->ready_for_next_read();
}
template <class packet_decoder,
	typename B1,
	typename A1>
bool tcp_connection::launch(void (*data_recv_callback)(
			tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error, B1 b1),
		A1 a1){
    boost::recursive_mutex::scoped_lock lck(this->mutex_);
    this->disable_ = false;
    this->pkt_decoder_ptr_ = eyas::gc::create_new<packet_decoder>();
    this->dat_handler_ptr_ = generate_handler(
            boost::bind(data_recv_callback, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, a1)
        );
    this->ready_for_next_read();
}
template <class packet_decoder,
	typename B1, typename B2,
	typename A1, typename A2>
bool tcp_connection::launch(void (*data_recv_callback)(
			tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error,
			B1 b1, B2 b2),
		A1 a1, A2 a2){
    boost::recursive_mutex::scoped_lock lck(this->mutex_);
    this->disable_ = false;
    this->pkt_decoder_ptr_ = eyas::gc::create_new<packet_decoder>();
    this->dat_handler_ptr_ = generate_handler(
            boost::bind(data_recv_callback, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, a1, a2)
        );
    this->ready_for_next_read();
}
template <class packet_decoder,
	typename B1, typename B2, typename B3,
	typename A1, typename A2, typename A3>
bool tcp_connection::launch(void (*data_recv_callback)(
			tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error,
			B1 b1, B2 b2, B3 b3),
		A1 a1, A2 a2, A3 a3){
    boost::recursive_mutex::scoped_lock lck(this->mutex_);
    this->disable_ = false;
    this->pkt_decoder_ptr_ = eyas::gc::create_new<packet_decoder>();
    this->dat_handler_ptr_ = generate_handler(
            boost::bind(data_recv_callback, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, a1, a2, a3)
        );
    this->ready_for_next_read();
}



template <class packet_decoder,
	class T, typename T_Obj>
bool tcp_connection::launch(void (T::*data_recv_callback)(
			tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error),
		T_Obj obj){
    boost::recursive_mutex::scoped_lock lck(this->mutex_);
    this->disable_ = false;
    this->pkt_decoder_ptr_ = eyas::gc::create_new<packet_decoder>();
    this->dat_handler_ptr_ = generate_handler(
            boost::bind(data_recv_callback, obj, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)
        );
    this->ready_for_next_read();
}

template <class packet_decoder,
	class T, typename T_Obj,
	typename B1,
	typename A1>
bool tcp_connection::launch(void (T::*connected_callback)(
			tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error,
			B1 b1),
		T_Obj obj,
		A1 a1){
    boost::recursive_mutex::scoped_lock lck(this->mutex_);
    this->disable_ = false;
    this->pkt_decoder_ptr_ = eyas::gc::create_new<packet_decoder>();
    this->dat_handler_ptr_ = generate_handler(
            boost::bind(data_recv_callback, obj, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, a1)
        );
    this->ready_for_next_read();
}

template <class packet_decoder,
	class T, typename T_Obj,
	typename B1, typename B2,
	typename A1, typename A2>
bool tcp_connection::launch(void (T::*connected_callback)(
			tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error,
			B1 b1, B2 b2),
		T_Obj obj,
		A1 a1, A2 a2){
    boost::recursive_mutex::scoped_lock lck(this->mutex_);
    this->disable_ = false;
    this->pkt_decoder_ptr_ = eyas::gc::create_new<packet_decoder>();
    this->dat_handler_ptr_ = generate_handler(
            boost::bind(data_recv_callback, obj, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, a1, a2)
        );
    this->ready_for_next_read();
}

template <class packet_decoder,
	class T, typename T_Obj,
	typename B1, typename B2, typename B3,
	typename A1, typename A2, typename A3>
bool tcp_connection::launch(void (T::*connected_callback)(
			tcp_connection_ptr const & conn_ptr, eyas::buffer::iobuffer& data, const eyas::io::error_code &error,
			B1 b1, B2 b2, B3 b3),
		T_Obj obj,
		A1 a1, A2 a2, A3 a3){
    boost::recursive_mutex::scoped_lock lck(this->mutex_);
    this->disable_ = false;
    this->pkt_decoder_ptr_ = eyas::gc::create_new<packet_decoder>();
    this->dat_handler_ptr_ = generate_handler(
            boost::bind(data_recv_callback, obj, this->shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, a1, a2, a3)
        );
    this->ready_for_next_read();
}


} /* namespace thread */
} /* namespace eyas */
#endif /* EYAS_SOURCES_IO_SOCKET_TCP_TCP_CONNECTION_H_ */
